refactor: replace Timeout options with context.Context#126
Conversation
- Add context.Context as first parameter to all public functions/methods that perform I/O (git command execution) - Remove Timeout field from all Options structs and the Command struct - Remove WithTimeout, SetTimeout, RunInDirWithTimeout, RunInDirPipelineWithTimeout, and RunWithTimeout methods - Auto-apply DefaultTimeout (1 minute) when context has no deadline - Rename UploadPackOptions.Timeout to InactivityTimeout (git protocol flag) - Remove timeout parameter from IsURLAccessible (use context.WithTimeout) - Keep ErrExecTimeout for deadline exceeded errors - Update all test files accordingly
There was a problem hiding this comment.
Pull request overview
This PR refactors the library’s Git I/O surface to be context.Context-driven instead of using per-call Timeout fields, with a default timeout applied when callers don’t set a deadline.
Changes:
- Adds
context.Contextas the first parameter to public APIs that execute Git commands. - Removes
TimeoutfromCommandand mostOptionsstructs; relies on context deadlines +DefaultTimeout. - Updates tests and call sites to pass context and to use
context.WithTimeoutwhere needed.
Reviewed changes
Copilot reviewed 43 out of 43 changed files in this pull request and generated 13 comments.
Show a summary per file
| File | Description |
|---|---|
| blob.go | Make blob reading methods context-aware (Bytes, Pipeline). |
| blob_test.go | Update blob tests to pass context. |
| command.go | Refactor command execution to be context-driven; remove timeout fields/options. |
| command_test.go | Update command tests to use context timeouts and new NewCommand signature. |
| commit.go | Thread context through commit I/O helpers (parents, logs, diff lookups, image detection). |
| commit_archive.go | Add context to archive creation. |
| commit_archive_test.go | Update archive tests for context. |
| commit_submodule.go | Add context to submodule discovery and lookups. |
| commit_submodule_test.go | Update submodule tests for context. |
| commit_test.go | Update commit tests for context-aware APIs. |
| git.go | Make BinVersion context-aware. |
| git_test.go | Update TestMain setup to use context-aware clone. |
| repo.go | Thread context through repository init/clone and commit parsing helpers. |
| repo_blame.go | Make blame context-aware and pass context to dependent calls. |
| repo_blame_test.go | Update blame tests for context. |
| repo_blob.go | Make CatFileBlob context-aware (options updated). |
| repo_blob_test.go | Update blob tests for context. |
| repo_commit.go | Make commit-related repository APIs context-aware (log, rev-list, cat-file, etc.). |
| repo_commit_test.go | Update commit repo tests for context. |
| repo_diff.go | Make diff APIs context-aware and use context-based command execution. |
| repo_diff_test.go | Update diff tests for context. |
| repo_grep.go | Make grep context-aware. |
| repo_grep_test.go | Update grep tests for context. |
| repo_pull.go | Make merge-base context-aware. |
| repo_pull_test.go | Update merge-base tests for context. |
| repo_reference.go | Make reference APIs context-aware (show-ref, branches/tags existence checks). |
| repo_reference_test.go | Update reference tests for context. |
| repo_remote.go | Make remote APIs context-aware and update URL accessibility helper accordingly. |
| repo_remote_test.go | Update remote tests for context. |
| repo_tag.go | Make tag APIs context-aware (tag lookup/list/create/delete). |
| repo_tag_test.go | Update tag tests for context. |
| repo_test.go | Update core repo tests (init/clone/fetch/pull/push/etc.) for context. |
| repo_tree.go | Make LsTree context-aware and remove timeout usage. |
| repo_tree_test.go | Update tree tests for context. |
| server.go | Make server-side commands (update-server-info/receive-pack/upload-pack) context-aware. |
| server_test.go | Update server tests for context. |
| tag.go | Make Tag.Commit context-aware. |
| tag_test.go | Update tag tests for context. |
| tree.go | Make tree traversal/listing (Subtree, Entries) context-aware. |
| tree_blob.go | Make TreeEntry context-aware; some tree->blob helpers still context-less internally. |
| tree_blob_test.go | Update tree/blob tests for context. |
| tree_entry.go | Thread context into entries/commits info and remove timeout propagation. |
| tree_entry_test.go | Update tree entry tests for context. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Fix typos: refsepc->refspec, direcotry->directory, exsit->exist - Rename TestCommand_RunWithTimeout to TestCommand_RunWithContextTimeout - Distinguish context.DeadlineExceeded from context.Canceled in command execution (only map DeadlineExceeded to ErrExecTimeout) - Add context.Context param to Tree.Blob, Tree.BlobByIndex, and TreeEntry.Size, removing all context.TODO() usage - Replace sync.Once with sync.Mutex in Tree.Entries and Commit.Submodules to only cache successful results, allowing retries with a fresh context on failure - Plumb CommandOptions through CatFileBlob's underlying RevParse and CatFileType calls
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 43 out of 43 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Replace sync.Once with mutex+success-only caching in BinVersion - Normalize cmd.Wait() errors when context expires concurrently in the select result branch - Fix Run() doc comment: remove incorrect 'in string' - Fix potential panic in .gitmodules parsing: use strings.Cut instead of strings.Split to safely handle malformed lines - Check bufio.Scanner.Err() after scan loop to catch I/O errors
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 43 out of 43 changed files in this pull request and generated no new comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 43 out of 43 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (2)
tree_entry.go:104
- TreeEntry.Size uses sync.Once and silently returns 0 on any error. If the first call fails (e.g. context deadline exceeded), the 0 value is cached forever and subsequent calls cannot retry with a fresh context, which can produce incorrect sizes and downstream behavior (e.g. Blob.Bytes preallocation). Consider switching to the same pattern used by Tree.Entries/Commit.Submodules (mutex + sizeSet flag) so only successful lookups are cached, or otherwise avoid caching failed attempts.
command.go:143 - When log output is enabled (logOutput != nil), RunInDirWithOptions wraps opt.Stdout in limitDualWriter but still writes to the underlying writer (w.w) unconditionally. If the caller passes a nil Stdout (valid for exec.Cmd), this will panic once the command writes output. Consider defaulting a nil Stdout to io.Discard (both for cmd.Stdout and the wrapped writer) when logging is enabled.
buf := new(bytes.Buffer)
w := opt.Stdout
if logOutput != nil {
buf.Grow(512)
w = &limitDualWriter{
W: buf,
N: int64(buf.Cap()),
w: opt.Stdout,
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Cap the Size value before passing to bytes.Buffer.Grow to prevent panic on very large blobs where int64 overflows int.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 43 out of 43 changed files in this pull request and generated 3 comments.
Comments suppressed due to low confidence (1)
tree_entry.go:103
TreeEntry.Sizeusessync.Once, so if the first call fails (e.g., context deadline exceeded / transient git error),e.sizeremains 0 and the failure is effectively cached forever. Consider switching to a mutex +sizeSetflag (likeTree.Entries/BinVersion) so the size is only cached after a successfulcat-file -s+ parse, and callers can retry with a fresh context.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Kill process before waiting in ctx.Done branch to enforce cancellation promptly (was dead code after Wait returned) - Fix test name grammar: 'files have' -> 'files that have the' - Collapse single-import block to idiomatic 'import "context"'
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 43 out of 43 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
tree_entry.go:106
- The
Size()method usessync.Oncefor caching but takes acontext.Contextparameter. This creates an inconsistency: the context is only used on the first invocation (insidesizeOnce.Do), and subsequent calls ignore the provided context and return the cached value.
This differs from the pattern used elsewhere in this PR (e.g., Tree.Entries, Commit.Submodules, BinVersion) where failed attempts are not cached, allowing retries with a fresh context. If the first Size() call fails due to context cancellation or timeout, the error is silently ignored (line 100-101 just returns), and subsequent calls will return 0.
Consider either:
- Changing to the mutex+flag pattern used elsewhere so failed attempts can be retried, or
- Documenting this behavior clearly in the method comment
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 43 out of 43 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
tree_entry.go:107
- The
Size()method still usessync.Oncefor caching, which is inconsistent with the new caching pattern used elsewhere in this PR. Other similar methods (Tree.Entries(),Commit.Submodules(),BinVersion()) now use a mutex+flag pattern that doesn't cache failures, allowing retries with a fresh context.
Consider refactoring TreeEntry.Size() to use the same mutex+flag pattern:
- Change
sizeOnce sync.OncetosizeMu sync.MutexandsizeSet bool - Lock/unlock the mutex and check
sizeSetflag - Only set
sizeSet = trueon successful execution - This would allow retries if the git command fails (e.g., due to context timeout)
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Add comment explaining type conversion pattern in CatFileBlob - Replace sync.Once with mutex+success-only caching in TreeEntry.Size so failed attempts (e.g. context timeout) are not cached permanently
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 43 out of 43 changed files in this pull request and generated no new comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Test deadline fires mid-execution (process killed, returns ErrExecTimeout) - Test context cancellation returns context.Canceled, not ErrExecTimeout - Test default timeout is applied when context has no deadline - Use blockingReader helper that unblocks on channel close so cmd.Wait can return cleanly after process is killed
Summary
context.Contextas first parameter to all public functions/methods that perform I/O (git command execution)Timeoutfield from all Options structs and theCommandstructDefaultTimeout(1 minute) when context has no deadline setBreaking changes
context.Contextas first parameterTimeoutfield from all Options structs (~20 structs)WithTimeout,SetTimeout,RunInDirWithTimeout,RunInDirPipelineWithTimeout,RunWithTimeoutUploadPackOptions.Timeoutrenamed toInactivityTimeout(it maps to git's--timeoutprotocol flag, not execution timeout)IsURLAccessibleno longer accepts atimeout time.Durationparameter — callers should usecontext.WithTimeoutinsteadMigration guide
Before:
After:
If no deadline is needed, pass
context.Background()— a default 1-minute timeout is applied automatically.Details
RunInDirWithOptionschecksctx.Deadline()— if no deadline is set, it wraps the context withDefaultTimeout(1 minute) automaticallyErrExecTimeoutis still returned when context deadline is exceeded (including whencmd.Start()fails due to an already-expired context)Tag.ID(),DiffFile.Name()) do not take contextparseCommit,parseTag, etc.) do not take contextOpen()and hook-related methods remain unchanged (filesystem only)Testing
All existing tests updated and passing:
go test ./...✅